{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "51c97544-aee1-493d-94c5-0db0c4db6a96",
   "metadata": {},
   "source": [
    "# Channel Models from Datasets"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c854a11-320e-4d9d-bf4b-3b60e2d408f6",
   "metadata": {},
   "source": [
    "In this notebook, you will learn how to create a channel model from a [generator](https://wiki.python.org/moin/Generators). This can be used, e.g., to import datasets of channel impulse responses."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f25c031b-7c18-4942-8301-781e9555e000",
   "metadata": {},
   "source": [
    "* [GPU Configuration and Imports](#GPU-Configuration-and-Imports)\n",
    "* [Simulation Parameters](#Simulation-Parameters)\n",
    "* [Creating a Simple Dataset](#Creating-a-Simple-Dataset)\n",
    "* [Generators](#Generators)\n",
    "* [Use the Channel Model for OFDM Transmissions](#Use-the-Channel-Model-for-OFDM-Transmissions)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0c41452-1a20-4b53-a02f-a2981ee42836",
   "metadata": {},
   "source": [
    "## GPU Configuration and Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "bac912ac-4a02-4e58-a697-e53d021852a5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:03.029085Z",
     "iopub.status.busy": "2025-03-09T01:02:03.028454Z",
     "iopub.status.idle": "2025-03-09T01:02:05.488798Z",
     "shell.execute_reply": "2025-03-09T01:02:05.487880Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "if os.getenv(\"CUDA_VISIBLE_DEVICES\") is None:\n",
    "    gpu_num = 0 # Use \"\" to use the CPU\n",
    "    os.environ[\"CUDA_VISIBLE_DEVICES\"] = f\"{gpu_num}\"\n",
    "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n",
    "\n",
    "# Import Sionna\n",
    "try:\n",
    "    import sionna.phy\n",
    "except ImportError as e:\n",
    "    import sys\n",
    "    if 'google.colab' in sys.modules:\n",
    "       # Install Sionna in Google Colab\n",
    "       print(\"Installing Sionna and restarting the runtime. Please run the cell again.\")\n",
    "       os.system(\"pip install sionna\")\n",
    "       os.kill(os.getpid(), 5)\n",
    "    else:\n",
    "       raise e\n",
    "\n",
    "# Configure the notebook to use only a single GPU and allocate only as much memory as needed\n",
    "# For more details, see https://www.tensorflow.org/guide/gpu\n",
    "import tensorflow as tf\n",
    "gpus = tf.config.list_physical_devices('GPU')\n",
    "if gpus:\n",
    "    try:\n",
    "        tf.config.experimental.set_memory_growth(gpus[0], True)\n",
    "    except RuntimeError as e:\n",
    "        print(e)\n",
    "# Avoid warnings from TensorFlow\n",
    "tf.get_logger().setLevel('ERROR')\n",
    "\n",
    "import numpy as np\n",
    "import h5py\n",
    "\n",
    "config = sionna.phy.config\n",
    "config.seed = 42 # Set seed for reproducible random number generation\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d02a418-ff93-4d27-a519-7c9e563ae91c",
   "metadata": {},
   "source": [
    "## Simulation Parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "01c892be-713d-401a-b6de-e984fd426d6f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:05.493043Z",
     "iopub.status.busy": "2025-03-09T01:02:05.492598Z",
     "iopub.status.idle": "2025-03-09T01:02:05.496655Z",
     "shell.execute_reply": "2025-03-09T01:02:05.495999Z"
    }
   },
   "outputs": [],
   "source": [
    "num_rx = 2\n",
    "num_rx_ant = 2\n",
    "num_tx = 1\n",
    "num_tx_ant = 8\n",
    "num_time_steps = 100\n",
    "num_paths = 10"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "632ffb75-c8d0-47e2-9861-ad12daffa20b",
   "metadata": {},
   "source": [
    "## Creating a Simple Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4bd653e-59b6-4a22-8201-3d1d7024ca6d",
   "metadata": {},
   "source": [
    "To illustrate how to load dataset, we will first create one.\n",
    "\n",
    "The next cell creates a very small HDF5 file storing Gaussian distributed i.i.d. path coefficients and uniformly distributed i.i.d. path delays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a54379aa-b75c-448a-b5f8-4e97ebd1d282",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:05.500440Z",
     "iopub.status.busy": "2025-03-09T01:02:05.500156Z",
     "iopub.status.idle": "2025-03-09T01:02:06.772744Z",
     "shell.execute_reply": "2025-03-09T01:02:06.771927Z"
    }
   },
   "outputs": [],
   "source": [
    "# Number of examples in the dataset\n",
    "dataset_size = 1000\n",
    "\n",
    "# Random path coefficients\n",
    "a_shape = [dataset_size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_paths, num_time_steps]\n",
    "a = (config.np_rng.normal(size=a_shape) + 1j*config.np_rng.normal(size=a_shape))/np.sqrt(2)\n",
    "\n",
    "# Random path delays\n",
    "tau = config.np_rng.uniform(size=[dataset_size, num_rx, num_tx, num_paths])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "206c14de-660a-4112-ad65-948595af540e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:06.776718Z",
     "iopub.status.busy": "2025-03-09T01:02:06.776417Z",
     "iopub.status.idle": "2025-03-09T01:02:07.023494Z",
     "shell.execute_reply": "2025-03-09T01:02:07.022461Z"
    }
   },
   "outputs": [],
   "source": [
    "filename = 'my_dataset.h5'\n",
    "hf = h5py.File(filename, 'w')\n",
    "hf.create_dataset('a', data=a)\n",
    "hf.create_dataset('tau', data=tau)\n",
    "hf.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e83cf683-609e-4d88-b26e-9c6df4bc2d3a",
   "metadata": {},
   "source": [
    "## Generators"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07ed9063-4d5b-4097-8b57-9005dcb9b597",
   "metadata": {},
   "source": [
    "The first step to load a dataset is to create a [generator](https://wiki.python.org/moin/Generators).\n",
    "A generator is a callable object, i.e., a function or a class that implements the `__call__()` method, and that behaves like an iterator.\n",
    "\n",
    "The next cell shows how to create a generator that parses an HDF5 file storing path coefficients and delays.\n",
    "Note that how the HDF5 file is parsed depends on its structure. The following generator is specific to the dataset previously created.\n",
    "\n",
    "If you have another dataset, you will need to change the way it is parsed in the generator. The generator can also carry out any type of desired pre-processing of your data, e.g., normalization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "787f5ba7-38ae-4ecd-8dde-028e45eb6a39",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.027539Z",
     "iopub.status.busy": "2025-03-09T01:02:07.027249Z",
     "iopub.status.idle": "2025-03-09T01:02:07.031813Z",
     "shell.execute_reply": "2025-03-09T01:02:07.031095Z"
    }
   },
   "outputs": [],
   "source": [
    "class HD5CIRGen:\n",
    "    def __init__(self, filename):\n",
    "        self.filename = filename\n",
    "\n",
    "    def __call__(self):\n",
    "        with h5py.File(self.filename, 'r') as hf:\n",
    "            for im in zip(hf[\"a\"], hf[\"tau\"]):\n",
    "                a = im[0]\n",
    "                tau = im[1]\n",
    "                # One could do some preprocessing on the dataset here\n",
    "                # ...\n",
    "                yield im"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7584d077-2261-4dab-bf16-fd5ac3b93901",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.035424Z",
     "iopub.status.busy": "2025-03-09T01:02:07.035138Z",
     "iopub.status.idle": "2025-03-09T01:02:07.038194Z",
     "shell.execute_reply": "2025-03-09T01:02:07.037577Z"
    }
   },
   "outputs": [],
   "source": [
    "generator = HD5CIRGen(filename)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "113c36b6-dd8c-4aa0-9a9d-5dc4db7312e7",
   "metadata": {},
   "source": [
    "We can use the generator to sample the first 5 items of the dataset:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7a30d141-0f79-4bbe-9293-9d163e0e33b3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.040527Z",
     "iopub.status.busy": "2025-03-09T01:02:07.040240Z",
     "iopub.status.idle": "2025-03-09T01:02:07.049461Z",
     "shell.execute_reply": "2025-03-09T01:02:07.048620Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2, 2, 1, 8, 10, 100)\n",
      "(2, 1, 10)\n",
      "(2, 2, 1, 8, 10, 100)\n",
      "(2, 1, 10)\n",
      "(2, 2, 1, 8, 10, 100)\n",
      "(2, 1, 10)\n",
      "(2, 2, 1, 8, 10, 100)\n",
      "(2, 1, 10)\n",
      "(2, 2, 1, 8, 10, 100)\n",
      "(2, 1, 10)\n"
     ]
    }
   ],
   "source": [
    "i = 0\n",
    "for (a,tau) in generator():\n",
    "    print(a.shape)\n",
    "    print(tau.shape)\n",
    "    i = i + 1\n",
    "    if i == 5:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00146ccc-023e-4343-8a8e-75eea0faf5e9",
   "metadata": {},
   "source": [
    "Let us create a channel model from this dataset:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0fdaa876-cefd-433a-ad74-2d944192149a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.052498Z",
     "iopub.status.busy": "2025-03-09T01:02:07.052224Z",
     "iopub.status.idle": "2025-03-09T01:02:07.094274Z",
     "shell.execute_reply": "2025-03-09T01:02:07.093325Z"
    }
   },
   "outputs": [],
   "source": [
    "from sionna.phy.channel import CIRDataset\n",
    "\n",
    "batch_size = 64 # The batch_size cannot be changed after the creation of the channel model\n",
    "channel_model = CIRDataset(generator,\n",
    "                           batch_size,\n",
    "                           num_rx,\n",
    "                           num_rx_ant,\n",
    "                           num_tx,\n",
    "                           num_tx_ant,\n",
    "                           num_paths,\n",
    "                           num_time_steps)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "234582db-aad6-4150-812f-9ae86f83d780",
   "metadata": {},
   "source": [
    "We can now sample from this dataset in the same way as we would from a stochastic channel model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "84b6373a-0c2e-43b5-a201-f999e210d549",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.097655Z",
     "iopub.status.busy": "2025-03-09T01:02:07.097354Z",
     "iopub.status.idle": "2025-03-09T01:02:07.224723Z",
     "shell.execute_reply": "2025-03-09T01:02:07.222534Z"
    }
   },
   "outputs": [],
   "source": [
    "# Note that the arguments batch_size, num_time_steps, and smapling_frequency\n",
    "# of the __call__ function are ignored as they are already specified by the dataset.\n",
    "a, tau = channel_model()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "1a07eef8-540b-4434-a144-32d6da174454",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.229726Z",
     "iopub.status.busy": "2025-03-09T01:02:07.229395Z",
     "iopub.status.idle": "2025-03-09T01:02:07.238064Z",
     "shell.execute_reply": "2025-03-09T01:02:07.236498Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 2, 2, 1, 8, 10, 100)\n",
      "<dtype: 'complex64'>\n",
      "(64, 2, 1, 10)\n",
      "<dtype: 'float32'>\n"
     ]
    }
   ],
   "source": [
    "print(a.shape)\n",
    "print(a.dtype)\n",
    "print(tau.shape)\n",
    "print(tau.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9de862ae-001f-4eb9-8f73-9232373a9f96",
   "metadata": {},
   "source": [
    "## Use the Channel Model for OFDM Transmissions\n",
    "\n",
    "The following code demonstrates how you can use the channel model to generate channel frequency responses that can be used for the simulation of communication system based on OFDM."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "6865d499-7bd9-4d0b-9ba7-677df685d3b7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.243358Z",
     "iopub.status.busy": "2025-03-09T01:02:07.242952Z",
     "iopub.status.idle": "2025-03-09T01:02:07.273996Z",
     "shell.execute_reply": "2025-03-09T01:02:07.272161Z"
    }
   },
   "outputs": [],
   "source": [
    "# Create an OFDM resource grid\n",
    "# Each time step is assumed to correspond to one OFDM symbol over which it is constant.\n",
    "resource_grid = sionna.phy.ofdm.ResourceGrid(\n",
    "                                num_ofdm_symbols=num_time_steps,\n",
    "                                fft_size=76,\n",
    "                                subcarrier_spacing=15e3,\n",
    "                                num_tx=num_tx,\n",
    "                                num_streams_per_tx=num_tx_ant)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "cd862bcd-26e9-4774-97a2-0d32c2ba8613",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.279035Z",
     "iopub.status.busy": "2025-03-09T01:02:07.278743Z",
     "iopub.status.idle": "2025-03-09T01:02:07.289637Z",
     "shell.execute_reply": "2025-03-09T01:02:07.287694Z"
    }
   },
   "outputs": [],
   "source": [
    "ofdm_channel = sionna.phy.channel.GenerateOFDMChannel(channel_model, resource_grid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "6180d32d-c5e7-45bd-a71d-333f405a548b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.294054Z",
     "iopub.status.busy": "2025-03-09T01:02:07.293770Z",
     "iopub.status.idle": "2025-03-09T01:02:07.538912Z",
     "shell.execute_reply": "2025-03-09T01:02:07.537516Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 2, 2, 1, 8, 100, 76)\n"
     ]
    }
   ],
   "source": [
    "# Generate a batch of frequency responses\n",
    "# Shape: [batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_ofdm_symbols, num_subcarriers]\n",
    "h_freq = ofdm_channel()\n",
    "print(h_freq.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "3bcc8deb-db22-4b48-9ae0-489cb0f083df",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-03-09T01:02:07.543815Z",
     "iopub.status.busy": "2025-03-09T01:02:07.543336Z",
     "iopub.status.idle": "2025-03-09T01:02:08.138320Z",
     "shell.execute_reply": "2025-03-09T01:02:08.136306Z"
    }
   },
   "outputs": [],
   "source": [
    "# Delete dataset\n",
    "%rm my_dataset.h5"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
